/*
 * int_map.h
 *
 *  Created on: 2012-8-29
 *      Author: lin
 */

#ifndef INT_MAP_H_
#define INT_MAP_H_

#include "def/defs.h"
#include "zmalloc/zmalloc.h"

#define DEF_INT_MAP_CAPACITY     8
#define DEF_INT_MAP_FACTOR 0.75

#define INT_MAP_OK 0
#define INT_MAP_ERR 1
#define INT_MAP_ABSENT 2
#define INT_MAP_EXIST 3

typedef struct int_map_entry {
    mixed_t val;
    struct int_map_entry *next;
    uint32_t key;
} int_map_entry_t;

typedef struct {
    mixed_t_dup_fn val_dup_fn;
    mixed_t_free_fn val_free_fn;

    int_map_entry_t **table;

    uint32_t capacity;
    uint32_t capacity_mask;
    uint32_t size;
    uint32_t thresold;
    float factor;
} int_map_t;

static inline int_map_entry_t* _int_map_find_entry(uint32_t idx, int_map_entry_t** table, uint32_t key,
        int_map_entry_t** prev) {
    int_map_entry_t *eprev = NULL;
    for (int_map_entry_t *e = table[idx]; e; eprev = e, e = e->next) {
        if (e->key == key) {
            if (prev) {
                *prev = eprev;
            }
            return e;
        }
    }
    return NULL;
}
int_map_t* int_map_new0(uint32_t capacity, float factor);

static inline int_map_t* int_map_new(void) {
    return int_map_new0(DEF_INT_MAP_CAPACITY, DEF_INT_MAP_FACTOR);
}
void int_map_clean(int_map_t* imap);

static inline void int_map_free(int_map_t* imap) {
    int_map_clean(imap);
    zfree(imap);
}

void int_map_rehash(int_map_t* imap, uint32_t newcapacity);

int int_map_put_absent(int_map_t* imap, uint32_t key, mixed_t val);

static inline int int_map_put_absent_ptr(int_map_t *imap, uint32_t key,
        void *val) {
    mixed_t mv;
    mv.ptr = val;
    return int_map_put_absent(imap, key, mv);
}
int int_map_put(int_map_t* imap,uint32_t key, mixed_t val);

static inline int int_map_put_ptr(int_map_t *imap, uint32_t key, void *val) {
    mixed_t mv;
    mv.ptr = val;
    return int_map_put(hmap, key, mv);
}

int int_map_remove(int_map_t* imap, uint32_t key);
int int_map_remove_nofree(int_map_t* imap, uint32_t key);


static inline int int_map_get(int_map_t* imap, uint32_t key, mixed_t* val){
    int_map_entry_t* e = _int_map_find_entry(key&imap->capacity_mask, imap->table, key,NULL);
    if (!e) {
        return INT_MAP_ABSENT;
    }
    *val = e->val;
    return INT_MAP_EXIST;
}

static inline void* int_map_get_ptr(int_map_t *imap, uint32_t key) {
    mixed_t val;
    return int_map_get(imap,key,&val)==INT_MAP_EXIST?val.ptr:NULL;
}
int_map_entry_t* int_map_begin(int_map_t *hmap);
int_map_entry_t* int_map_next(int_map_t *hmap, int_map_entry_t* cur);

#define int_map_foreach(imap, entry) \
for(entry = int_map_begin(imap); entry; entry = int_map_next(imap, entry))

#define int_map_size(imap) (imap)->size
#define int_map_is_empty(imap) (!(imap)->size)
#define int_map_capacity(imap) (imap)->capacity

#define int_map_set_dup_val_fn(imap,fn) (hmap)->val_dup_fn = (fn)
#define int_map_set_free_val_fn(imap,fn) (hmap)->val_free_fn = (fn)
#endif /* INT_MAP_H_ */
